package com.mtung.dubbo.proxy;

import com.mtung.dubbo.discover.ServiceDiscover;
import com.mtung.dubbo.discover.ServiceDiscoverImpl;
import com.mtung.dubbo.handler.DubboClientHandler;
import com.mtung.dubbo.loadbalance.impl.LoopLoadBalance;
import com.mtung.dubbo.marshalling.MarshallingCodeCFactory;
import com.mtung.dubbo.req.RpcRequest;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.util.List;

/**
 * RPC客户端代理类,用来快递构建客户端并发送给dubbo服务端
 * @author : zhenguo.yao
 * @date : 2020/3/23 0023 16:10
 */
public class RpcClientProxy {
    /**
     * 把对应的接口类通过本方法组装成一个rpcRequest对象,
     * 然后发送给服务端
     * @param serviceClass
     * @param <T>
     * @return
     */
    public <T> T createClient(final Class<T> serviceClass){
        final Class[] classes = {serviceClass};
        return (T) Proxy.newProxyInstance(serviceClass.getClassLoader(), classes, new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                final ServiceDiscover serviceDiscover = new ServiceDiscoverImpl();
                final String serviceName = serviceClass.getName();
                final List<String> discover = serviceDiscover.getDiscover(serviceName);
                // 采用我们默认的轮训负载均衡器
                final LoopLoadBalance loopLoadBalance = new LoopLoadBalance();
                final String select = loopLoadBalance.select(discover);
                String[] split = URLDecoder.decode(select).split(":");
                String host = split[1].replace("//", "");
                String port = split[2];
                RpcRequest rpcRequest = new RpcRequest();
                rpcRequest.setClassName(serviceName);
                rpcRequest.setMethodName(method.getName());
                rpcRequest.setParameterTypes(method.getParameterTypes());
                rpcRequest.setParamsValue(args);
                // 启动我们的netty客户端发送消息
                return sendMsg(host, Integer.parseInt(port), rpcRequest);
            }
        });
    }


    private Object sendMsg(String host, int port, RpcRequest rpcRequest){
        final DubboClientHandler dubboClientHandler = new DubboClientHandler();

        final NioEventLoopGroup group = new NioEventLoopGroup();
        final Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .remoteAddress(new InetSocketAddress(host,port))
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
                        ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
                        ch.pipeline().addLast(dubboClientHandler);
                    }
                });
        try {
            // 发起同步连接
            final ChannelFuture future = bootstrap.connect().sync();
            // 客户端发送我们的对象
            System.out.println("生产者发送消息:" + rpcRequest.toString());
            future.channel().writeAndFlush(rpcRequest);
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            group.shutdownGracefully();
        }
        return dubboClientHandler.getResponse();
    }


}
